package com.lp.multi.thread.example06;

import java.util.concurrent.TransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: lp
 * @create: 2021-06-22 18:31
 * @description: 线程协作，生产者消费者模式--管程法
 */
public class ThreadTeamWorkTest {
    public static void main(String[] args) {
        Container container = new Container();
        new Producter(container,"p1").start();
        new Producter(container,"p2").start();
        new Producter(container,"p3").start();
        new Customer(container,"c1").start();
        new Customer(container,"c2").start();
        new Customer(container,"c3").start();
        new Customer(container,"c4").start();
        new Customer(container,"c5").start();
        new Customer(container,"c6").start();
        new Customer(container,"c7").start();
        new Customer(container,"c8").start();
    }

}

/**
 * 生产者
 */
class Producter extends Thread{
    private Container container;

    public Producter(Container container,String name) {
        this.setName(name);
        this.container = container;
    }

    @Override
    public void run() {
        long i =1;
        while (true) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                return;
            }
            try {
                for (int j = 0; j < 5; j++) {
                    String goodsName = Thread.currentThread().getName()+"_"+i++;
                    container.push(new Goods(goodsName));
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



}

/**
 * 消费者
 */
class Customer extends  Thread {
    private Container container;

    public Customer(Container container,String name) {
        this.container = container;
        this.setName(name);
    }

    @Override
    public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        return;
                    }
                    try {
                        System.out.println(this.getName()+"消费了:" +container.pop().getGoodsNo());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

    }
}

/**
 * 产品
 */
class Goods{
    private String goodsNo;

    public Goods(String goodsNo) {
        this.goodsNo = goodsNo;
    }

    public String getGoodsNo() {
        return goodsNo;
    }

    public void setGoodsNo(String goodsNo) {
        this.goodsNo = goodsNo;
    }
}

/**
 * 容器，也可以理解为队列
 */
class Container{
    /**
     * 容器存储数量最多只能10个
     */
    private Goods[] goodsArray = new Goods[10];
    private  int goodsNum = 0;

    public synchronized void push(Goods goods) throws InterruptedException {
            while(goodsArray.length == goodsNum){
                //队列满了，生产者等待
                    System.out.println(Thread.currentThread().getName()+"暂停,生产太快，消费者来不及消费");
                    //wait会释放锁，进入无限等待至被其他线程唤醒。
                   // wait必须放到死循环中，每次线程被唤醒后，需要重新判断wail循环条件.被唤醒进入的方法的线程只有一个。
                    this.wait();
            }

            goodsArray[goodsNum] = goods;
            goodsNum ++;
            System.out.println("push后,当前容器剩余商品个数:"+goodsNum);
            System.out.println(Thread.currentThread().getName()+"生产了:" + goods.getGoodsNo());
            //有商品了，通知消费者消费
            notify();
    }

    public synchronized Goods pop() throws InterruptedException {
            while(goodsNum == 0){
                //等待有产品
                System.out.println(Thread.currentThread().getName()+"消费太快，来不及生产");
                this.wait();
            }

            System.out.println("消费前个数,goodsNum = " + goodsNum);
            goodsNum --;
            System.out.println("消费后个数,当前容器剩余商品个数:"+goodsNum);
            Goods goods = goodsArray[goodsNum];
            //清空掉已经消费的商品
            goodsArray[goodsNum] =  null;
            //如果消费了，通知生产者生产
            notifyAll();
        return goods;
    }


}
